#include "ModelFactory.h"
#include "Graphics.h"
#include <cmath>

ModelFactory::ModelFactory(Config* config, Graphics* graphics) {
	this->config = config;
    this->graphics = graphics;
}

Primitive* ModelFactory::Cube(float faceLength, float x, float y, float z) {
	Primitive* prim = new Primitive(config, "", "cube", graphics);
	prim->ID = config->GetNewID();

    //generate indices
    int indexCounter = 0;
    for(int i=0; i<6; i++) {
        DWORD indices[6] = {0+indexCounter + graphics->vertexCounter,
                            1+indexCounter + graphics->vertexCounter,
                            2+indexCounter + graphics->vertexCounter,
                            2+indexCounter + graphics->vertexCounter,
                            1+indexCounter + graphics->vertexCounter,
                            3+indexCounter + graphics->vertexCounter};
        Shape::TextureFace* face = new Shape::TextureFace(indices, "", "",
														  XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f),
														  XMVectorSet(1.0f, 1.0f, 1.0f, 0.0f),
														  XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f));
        indexCounter += 4;
        graphics->allTextureFaces->push_back(face);
        prim->textureFaces->push_back(face);
    }

    //Generate vertices
/*1*/graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength/2), -1.0f*(faceLength/2), 1.0f*(faceLength/2), XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), 0.0f, 0.0f));
graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), -1.0f*(faceLength / 2), 1.0f*(faceLength / 2), XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), 0.0f, 1.0f));
graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength / 2), 1.0f*(faceLength / 2), 1.0f*(faceLength / 2), XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), 1.0f, 0.0f));
graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), 1.0f*(faceLength / 2), 1.0f*(faceLength / 2), XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), 1.0f, 1.0f));
/*2*/graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength / 2), -1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(0.0f, 0.0f, -1.0f, 0.0f), 0.0f, 0.0f));
graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength / 2), 1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(0.0f, 0.0f, -1.0f, 0.0f), 0.0f, 1.0f));
graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), -1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(0.0f, 0.0f, -1.0f, 0.0f), 1.0f, 0.0f));
graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), 1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(0.0f, 0.0f, -1.0f, 0.0f), 1.0f, 1.0f));
/*3*/graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength / 2), 1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), 0.0f, 0.0f));
graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength / 2), 1.0f*(faceLength / 2), 1.0f*(faceLength / 2), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), 0.0f, 1.0f));
graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), 1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), 1.0f, 0.0f));
graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), 1.0f*(faceLength / 2), 1.0f*(faceLength / 2), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), 1.0f, 1.0f));
/*4*/graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength / 2), -1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(0.0f, -1.0f, 0.0f, 0.0f), 0.0f, 0.0f));
graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), -1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(0.0f, -1.0f, 0.0f, 0.0f), 0.0f, 1.0f));
graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength / 2), -1.0f*(faceLength / 2), 1.0f*(faceLength / 2), XMVectorSet(0.0f, -1.0f, 0.0f, 0.0f), 1.0f, 0.0f));
graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), -1.0f*(faceLength / 2), 1.0f*(faceLength / 2), XMVectorSet(0.0f, -1.0f, 0.0f, 0.0f), 1.0f, 1.0f));
/*5*/graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), -1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f), 0.0f, 0.0f));
graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), 1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f), 0.0f, 1.0f));
graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), -1.0f*(faceLength / 2), 1.0f*(faceLength / 2), XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f), 1.0f, 0.0f));
graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), 1.0f*(faceLength / 2), 1.0f*(faceLength / 2), XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f), 1.0f, 1.0f));
/*6*/graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength / 2), -1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(-1.0f, 0.0f, 0.0f, 0.0f), 0.0f, 0.0f));
graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength / 2), -1.0f*(faceLength / 2), 1.0f*(faceLength / 2), XMVectorSet(-1.0f, 0.0f, 0.0f, 0.0f), 0.0f, 1.0f));
graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength / 2), 1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(-1.0f, 0.0f, 0.0f, 0.0f), 1.0f, 0.0f));
graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength / 2), 1.0f*(faceLength / 2), 1.0f*(faceLength / 2), XMVectorSet(-1.0f, 0.0f, 0.0f, 0.0f), 1.0f, 1.0f));
    
    graphics->vertexCounter += 24;

    prim->FaceLength   = faceLength;
	prim->Translation = XMVectorSet(x, y, z, 0.0f);
    return prim;
}

Primitive* ModelFactory::Plane(float faceLength, float x, float y, float z) {
    Primitive* prim = new Primitive(config, "", "plane", graphics);
    prim->ID = config->GetNewID();

    //generate indices
    int indexCounter = 0;
    for(int i=0; i<1; i++) {
        DWORD indices[6] = {0+indexCounter + graphics->vertexCounter,
                            1+indexCounter + graphics->vertexCounter,
                            2+indexCounter + graphics->vertexCounter,
                            2+indexCounter + graphics->vertexCounter,
                            1+indexCounter + graphics->vertexCounter,
                            3+indexCounter + graphics->vertexCounter};
		Shape::TextureFace* face = new Shape::TextureFace(indices, "", "", XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f), XMVectorSet(1.0f, 1.0f, 1.0f, 0.0f), XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f));
        indexCounter += 4;
        graphics->allTextureFaces->push_back(face);
        prim->textureFaces->push_back(face);
    }

    //Generate vertices
	/*1*/graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength / 2), 1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), 0.0f, 0.0f));
	graphics->allVertices->push_back(Shape::VERTEX(-1.0f*(faceLength / 2), 1.0f*(faceLength / 2), 1.0f*(faceLength / 2), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), 0.0f, 1.0f));
	graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), 1.0f*(faceLength / 2), -1.0f*(faceLength / 2), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), 1.0f, 0.0f));
	graphics->allVertices->push_back(Shape::VERTEX(1.0f*(faceLength / 2), 1.0f*(faceLength / 2), 1.0f*(faceLength / 2), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), 1.0f, 1.0f));
    
    graphics->vertexCounter += 4;

	prim->NrOfTextures = 0;
    prim->FaceLength = faceLength;
	prim->Translation = XMVectorSet(x, y, z, 0.0f);
    return prim;
}

Primitive* ModelFactory::Cylinder (float, float, float, float, float, float, int, int, int) {
	return NULL;
}

Primitive* ModelFactory::Disk (float, float, float, float, float, float, float, int, int, int) {
	return NULL;
}

//radius, centerX, centerY, centerZ, nrOfSlices, nrOfRings, gen
//slices = horizontal things, rings = vertical things
Primitive* ModelFactory::Sphere(float diameter, float centerX, float centerY, float centerZ, int nrOfSlices, int nrOfRings/*, int gen*/) {
	Primitive* prim = new Primitive(config, "", "sphere", graphics);
	prim->ID = config->GetNewID();

	int verticalSegments = nrOfSlices;
	int horizontalSegments = nrOfSlices * 2; //also allowed: nrOfRings

	//Generate indices
	int stride = horizontalSegments + 1;
	int indexCounter = 0;
	for (int i = 0; i < verticalSegments; i++) {
		for (int j = 0; j <= horizontalSegments; j++) {
			int nextI = i + 1;
			int nextJ = (j + 1) % stride;

			DWORD indices[6] = {
				(nextI * stride + nextJ)+ graphics->vertexCounter,
				(nextI * stride + j)	+ graphics->vertexCounter,
				(i * stride + nextJ)	+ graphics->vertexCounter,
				(i * stride + nextJ)	+ graphics->vertexCounter,
				(nextI * stride + j)	+ graphics->vertexCounter,
				(i * stride + j)		+ graphics->vertexCounter,
			};
			Shape::TextureFace* face = new Shape::TextureFace(indices, "", "", XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f), XMVectorSet(1.0f, 1.0f, 1.0f, 0.0f), XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f));
			indexCounter += 4;
			graphics->allTextureFaces->push_back(face);
			prim->textureFaces->push_back(face);
		}
	}

	float radius = diameter / 2;


	//Create a half vertical circle, from -1 to 1, like a vertical piece of orange
	for (int i=0; i<=verticalSegments; i++) {
		float v = 1 - (float)i/ verticalSegments; //v = 0, 0.1, 0.2, 0.3, ..., 1.0

		//latitude = -0.5PI, -0.4PI, -0.3PI, -0.2PI, -0.1PI, 0, 0.1PI, 0.2PI, ..., 0.5PI
		float latitude = (i * (float)XM_PI / verticalSegments) - (float)XM_PI / 2.0f;
		//dy goes from -1 to 0 to 1
		//dxz goes from 0 to 1 to 0
		//together they make up a vertical piece of orange
		float dy, dxz;

		dy = (float)sin((double)latitude);
		dxz = (float)cos((double)latitude);
		//XMScalarSinCos(&dy, &dxz, latitude); //computes both sine and cosine of latitude

		//Create a full horizontal circle at current point on the vertical half circle
		for (int j=0; j<=horizontalSegments; j++) { //j goes from 0 to 20
			//u = 0.05, 0.10, 0.15, 0.20, ..., 1.00
			float u = (float)j / horizontalSegments;

			//longitude = 0, 0.1PI, 0.2PI, 0.3PI, ..., 2.0PI
			float longitude = j * (float)(2 * XM_PI) / (float)horizontalSegments;
			float dx, dz;

			dx = (float)sin((double)longitude);
			dz = (float)cos((double)longitude);
			//XMScalarSinCos(&dx, &dz, longitude);

			dx *= dxz;
			dz *= dxz;

			//graphics->allVertices->push_back(Shape::VERTEX(-1.0f*((normal*radius).x), 1.0f*((normal*radius).y), 1.0f*((normal*radius).z), D3DXVECTOR3(normal.x, normal.y, normal.z), u, v));
			graphics->allVertices->push_back(Shape::VERTEX(dx*radius, dy*radius, dz*radius, XMVectorSet(dx, dy, dz, 0.0f), u, v));
		}
	}

	
	graphics->vertexCounter += verticalSegments * horizontalSegments; //increment vertexCounter AFTER inserting textureFaces!
	prim->NrOfTextures = 0;
	prim->Translation = XMVectorSet(centerX, centerY, centerZ, 0.0f);
	return prim;
}

Primitive* ModelFactory::Torus(float, float, float, float, float, int, int, int) {
	return NULL;
}